package top.v5it.japi.plus.sdk;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;

/**
 * AES对称加解密工具
 *
 * @author zhanpu
 * @date 2021/5/9
 */
@Slf4j
public class AesUtil {

    private AesUtil() {
        throw new UnsupportedOperationException();
    }

    //----生成对称密钥-----------------------------------------------------------------------------------------------------------//

    /**
     * 默认随机生成128位AES对称密钥
     *
     * @return 对称密钥二进制
     */
    public static byte[] generateKey() {
        return generateKey(128);
    }

    /**
     * 指定长度生成AES对称密钥
     *
     * @param keySize 密钥长度
     * @return 对称密钥二进制
     */
    public static byte[] generateKey(int keySize) {
        return SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), keySize).getEncoded();
    }

    //----AES加密文件-----------------------------------------------------------------------------------------------------------//

    /**
     * 文件加密
     *
     * @param srcFilepath 原文件路径
     * @param tagFilepath 目标文件路径
     * @param secretKey   对称密钥字符
     */
    public static void encryptFile(String srcFilepath, String tagFilepath, String secretKey) {
        encryptFile(new File(srcFilepath), new File(tagFilepath), secretKey);
    }

    /**
     * 文件加密
     *
     * @param srcFile   原文件
     * @param tagFile   目标文件
     * @param secretKey 对称密钥字符
     */
    public static void encryptFile(File srcFile, File tagFile, String secretKey) {
        encryptFile(srcFile, tagFile, Base64.decode(secretKey));
    }

    /**
     * 文件加密
     *
     * @param srcFile 原文件
     * @param tagFile 目标文件
     * @param key     对称密钥二进制
     */
    public static void encryptFile(File srcFile, File tagFile, byte[] key) {
        try (FileInputStream fileInputStream = IoUtil.toStream(srcFile)) {
            AES aes = SecureUtil.aes(key);
            byte[] encrypt = aes.encrypt(fileInputStream);
            FileUtil.writeBytes(encrypt, tagFile);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new IllegalStateException("文件加密失败");
        }
    }

    /**
     * 随机生成对称密钥进行文件加密，并返回密钥二进制
     *
     * @param srcFilepath 原文件路径
     * @param tagFilepath 目标文件路径
     * @return 对称密钥二进制
     */
    public static byte[] encryptFileOutKeyBinary(String srcFilepath, String tagFilepath) {
        byte[] key = generateKey();
        encryptFile(new File(srcFilepath), new File(tagFilepath), key);
        return key;
    }

    /**
     * 随机生成对称密钥进行文件加密，并返回密钥二进制
     *
     * @param srcFile 原文件
     * @param tagFile 目标文件
     * @return 对称密钥二进制
     */
    public static byte[] encryptFileOutKeyBinary(File srcFile, File tagFile) {
        byte[] key = generateKey();
        encryptFile(srcFile, tagFile, key);
        return key;
    }

    /**
     * 随机生成对称密钥进行文件加密，并返回密钥字符
     *
     * @param srcFilepath 原文件路径
     * @param tagFilepath 目标文件路径
     * @return 对称密钥字符
     */
    public static String encryptFileOutKeyStr(String srcFilepath, String tagFilepath) {
        return Base64.encode(encryptFileOutKeyBinary(srcFilepath, tagFilepath));
    }

    /**
     * 随机生成对称密钥进行文件加密，并返回密钥字符
     *
     * @param srcFile 原文件
     * @param tagFile 目标文件
     * @return 对称密钥字符
     */
    public static String encryptFileOutKeyStr(File srcFile, File tagFile) {
        return Base64.encode(encryptFileOutKeyBinary(srcFile, tagFile));
    }

    //----AES解密文件-----------------------------------------------------------------------------------------------------------//

    /**
     * 文件解密
     *
     * @param srcFilepath 原文件
     * @param tagFilepath 目标文件
     * @param secretKey   对称密钥
     */
    public static void decryptFile(String srcFilepath, String tagFilepath, String secretKey) {
        decryptFile(new File(srcFilepath), new File(tagFilepath), secretKey);
    }

    /**
     * 文件解密
     *
     * @param srcFile   原文件
     * @param tagFile   目标文件
     * @param secretKey 对称密钥字符
     */
    public static void decryptFile(File srcFile, File tagFile, String secretKey) {
        decryptFile(srcFile, tagFile, SecureUtil.decode(secretKey));
    }

    /**
     * 文件解密
     *
     * @param srcFile 原文件
     * @param tagFile 目标文件
     * @param key     对称密钥二进制
     */
    public static void decryptFile(File srcFile, File tagFile, byte[] key) {
        try (FileInputStream fileInputStream = IoUtil.toStream(srcFile)) {
            AES aes = SecureUtil.aes(key);
            byte[] decrypt = aes.decrypt(fileInputStream);
            FileUtil.writeBytes(decrypt, tagFile);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new IllegalStateException("文件解密失败");
        }
    }

    //----AES加密-----------------------------------------------------------------------------------------------------------//

    /**
     * 对称加密
     *
     * @param source    原数据
     * @param secretKey 对称密钥字条
     * @return 密文字符
     */
    public static String encrypt(String source, String secretKey) {
        return encrypt(source, SecureUtil.decode((secretKey)));
    }

    /**
     * 对称加密
     *
     * @param source 原数据
     * @param key    对称密钥二进制
     * @return 密文字符
     */
    public static String encrypt(String source, byte[] key) {
        return SecureUtil.aes(key).encryptBase64(source);
    }

    //----AES解密-----------------------------------------------------------------------------------------------------------//

    /**
     * 对称解密
     *
     * @param source    原数据
     * @param secretKey 对称密钥字条
     * @return 明文字符
     */
    public static String decrypt(String source, String secretKey) {
        return decrypt(source, SecureUtil.decode(secretKey));
    }

    /**
     * 对称解密
     *
     * @param source 原数据
     * @param key    对称密钥二进制
     * @return 明文字符
     */
    public static String decrypt(String source, byte[] key) {
        return SecureUtil.aes(key).decryptStr(source);
    }
}
